﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Sunny.Entity;
using Sunny.Model;
using Sunny.Model.RequestParam;
using Sunny.Model.Vo;
using Sunny.Service;
using Sunny.Util.Exception;

namespace Sunny.Service
{
    public class MenuService
    {
        private readonly SunnyContext _sunnyContext;

        public MenuService(SunnyContext sunnyContext)
        {
            _sunnyContext = sunnyContext;
        }

        public async Task<List<RouteVo>> GetTreeMenu(SysUser user)
        {
            List<SysMenu> sysMenus = new List<SysMenu>();
            string[] menuType = new[] {"M", "C"};
            var menus = _sunnyContext.SysMenus.Where(t => menuType.Contains(t.MenuType));
            if (user.IsAdmin())
            {
                sysMenus = await menus.ToListAsync();
            }
            else
            {
                var roleIds = await _sunnyContext.SysUserRoles.Where(t => t.UserId == user.UserId).Select(t => t.RoleId)
                    .ToListAsync();
                var menuIds = await _sunnyContext.SysRoleMenus.Where(t => roleIds.Contains(t.RoleId))
                    .Select(t => t.MenuId).ToListAsync();
                sysMenus = await _sunnyContext.SysMenus.Where(t => menuIds.Contains(t.MenuId)).ToListAsync();
            }
            
            
            List<RouteVo> routeVos = BuildMenus(sysMenus,0);
            return routeVos;
        }

        private List<RouteVo> BuildMenus(List<SysMenu> sysMenus,long parentId)
        {
            List<RouteVo> routers = new List<RouteVo>();
            var currentMenus = sysMenus.Where(t => t.ParentId == parentId);
            foreach (SysMenu menu in currentMenus)
            {
                RouteVo routeVo = new RouteVo();
                routeVo.Hidden = "0".Equals(menu.Visible);
                routeVo.Name = GetRouteName(menu);
                routeVo.Path = GetRouterPath(menu);
                routeVo.Component = GetComponent(menu);
                routeVo.Meta = new RouteMetaVo(menu.MenuName, menu.Icon, menu.IsCache == 1);

                List<SysMenu> cMenus = sysMenus.Where(t => t.ParentId == menu.MenuId).ToList();
                if (cMenus.Count > 0 && UserConstants.TYPE_DIR.Equals(menu.MenuType))
                {
                    routeVo.AlwaysShow = true;
                    routeVo.Redirect = "noRedirect";
                    routeVo.Children = BuildMenus(sysMenus,menu.MenuId);
                }
                else if (IsMeunFrame(menu))
                {
                    List<RouteVo> childrenList = new List<RouteVo>();
                    RouteVo children = new RouteVo();
                    children.Path = menu.Path;
                    children.Component = menu.Component;
                    children.Name = StringHelper.Capitalize(menu.Path);
                    children.Meta = new RouteMetaVo(menu.MenuName, menu.Icon, menu.IsCache == 1);

                    childrenList.Add(children);
                    routeVo.Children = childrenList;
                }

                routers.Add(routeVo);
            }

            return routers;
        }


        /// <summary>
        /// 获取路由名称
        /// </summary>
        /// <param name="menu"></param>
        /// <returns></returns>
        public string GetRouteName(SysMenu menu)
        {
            string routerName = StringHelper.Capitalize(menu.Path);
            // 非外链并且是一级目录（类型为目录）
            if (IsMeunFrame(menu))
            {
                routerName = "";
            }

            return routerName;
        }


        /// <summary>
        /// 获取路由地址
        /// </summary>
        /// <param name="menu"></param>
        /// <returns></returns>
        private string GetRouterPath(SysMenu menu)
        {
            string routerPath = menu.Path;
            // 非外链并且是一级目录（类型为目录）
            if (0 == menu.ParentId && UserConstants.TYPE_DIR.Equals(menu.MenuType)
                                   && menu.IsFrame.HasValue && UserConstants.NO_FRAME.Equals(menu.IsFrame.Value.ToString()))
            {
                routerPath = "/" + menu.Path;
            }
            // 非外链并且是一级目录（类型为菜单）
            else if (IsMeunFrame(menu))
            {
                routerPath = "/";
            }

            return routerPath;
        }

        /// <summary>
        /// 获取组件信息
        /// </summary>
        /// <param name="menu"></param>
        /// <returns></returns>
        private string GetComponent(SysMenu menu)
        {
            string component = UserConstants.LAYOUT;
            if (!string.IsNullOrEmpty(menu.Component) && !IsMeunFrame(menu))
            {
                component = menu.Component;
            }
            else if (string.IsNullOrEmpty(menu.Component) && IsParentView(menu))
            {
                component = UserConstants.PARENT_VIEW;
            }

            return component;
        }


        /// <summary>
        /// 是否为菜单内部跳转
        /// </summary>
        /// <param name="menu"></param>
        /// <returns></returns>
        private bool IsMeunFrame(SysMenu menu)
        {
            return menu.ParentId == 0 && UserConstants.TYPE_MENU.Equals(menu.MenuType)
                                      && menu.IsFrame.Equals(UserConstants.NO_FRAME);
        }

        /// <summary>
        /// 是否为parent_view组件
        /// </summary>
        /// <param name="menu"></param>
        /// <returns></returns>
        private bool IsParentView(SysMenu menu)
        {
            return menu.ParentId != 0 && UserConstants.TYPE_DIR.Equals(menu.MenuType);
        }

        public async Task<List<MenuTreeVo>> TreeSelect()
        {
            var menuList = await _sunnyContext.SysMenus.ToListAsync();
            return BuildTreeSelect(menuList,0);
        }

        private List<MenuTreeVo> BuildTreeSelect(List<SysMenu> menuList,long parentId)
        {
            List<MenuTreeVo> listTree = new List<MenuTreeVo>();
            var currentMenus = menuList.Where(t=>t.ParentId == parentId);
            foreach (var currentMenu in currentMenus)
            {
                MenuTreeVo menuTreeVo = new MenuTreeVo();
                menuTreeVo.Id = currentMenu.MenuId;
                menuTreeVo.Label = currentMenu.MenuName;
                menuTreeVo.Children = BuildTreeSelect(menuList,currentMenu.MenuId);
                listTree.Add(menuTreeVo);
            }
            return listTree;
        }

        public async Task<RoleMenuTreeSelectVo> RoleMenuTreeSelect(long roleId)
        {
            RoleMenuTreeSelectVo roleMenuTreeSelectVo = new RoleMenuTreeSelectVo();
            roleMenuTreeSelectVo.Menus = await this.TreeSelect();
            roleMenuTreeSelectVo.CheckedKeys = await _sunnyContext.SysRoleMenus.Where(t=>t.RoleId == roleId).Select(t=>t.MenuId).ToListAsync();
            
            return roleMenuTreeSelectVo;
        }

        public async Task<List<SysMenu>> MenuList(MenuListParam param)
        {
            var menus = _sunnyContext.SysMenus.AsQueryable();
            if(!string.IsNullOrEmpty(param.MenuName))
            {
                menus = menus.Where(t=>t.MenuName.Contains(param.MenuName));
            }
            if(!string.IsNullOrEmpty(param.Status))
            {
                menus = menus.Where(t=>t.Status == param.Status);
            }
            return await menus.OrderBy(t=>t.MenuId).ToListAsync();
        }

        public async Task Add(SysMenu sysMenu)
        {
            await _sunnyContext.SysMenus.AddAsync(sysMenu);
            await _sunnyContext.SaveChangesAsync();
        }

        public async Task<SysMenu> GetById(long menuId)
        {
            return await _sunnyContext.SysMenus.SingleOrDefaultAsync(t=>t.MenuId == menuId);
        }

        public async Task Update(SysMenu sysMenu)
        {
           var menu =  await _sunnyContext.SysMenus.SingleOrDefaultAsync(t=>t.MenuId == sysMenu.MenuId);
           menu.Component = sysMenu.Component;
           menu.Icon = sysMenu.Icon;
           menu.IsCache = sysMenu.IsCache;
           menu.IsFrame = sysMenu.IsFrame;
           menu.MenuName=sysMenu.MenuName;
           menu.MenuType = sysMenu.MenuType;
           menu.OrderNum = sysMenu.OrderNum;
           menu.ParentId = sysMenu.ParentId;
           menu.Path = sysMenu.Path;
           menu.Remark = sysMenu.Remark;
           menu.Visible = sysMenu.Visible;
           await _sunnyContext.SaveChangesAsync();
        }

        public async Task Delete(long menuId)
        {
            var hasChild = await _sunnyContext.SysMenus.AnyAsync(t=>t.ParentId == menuId);
            if(hasChild)
            {
                throw new BusinessException(CodeMessage.MenuHasChild);
            }
            var hasRole = await _sunnyContext.SysRoleMenus.AnyAsync(t=>t.MenuId == menuId && _sunnyContext.SysRoles.Select(r=>r.RoleId).Contains(t.RoleId));
            if(hasRole)
            {
                throw new BusinessException(CodeMessage.MenuHasRole);
            }
            
            SysMenu sysMenu = new SysMenu(){MenuId =  menuId};
            _sunnyContext.Attach(sysMenu).State = EntityState.Deleted;
            await _sunnyContext.SaveChangesAsync();
        }
    }
}